<?php

namespace app\common\library;

use think\App;
use think\Config;
use think\Log;
use think\Env;
use think\Db;
use app\admin\model\sport\Area as AreaModel;
use app\admin\model\sport\Score as ScoreModel;
use app\admin\model\sport\SUserDetail as ScoreUserDetailModel;
use app\admin\model\sport\Feedback as FeedbackModel;

/**
 * 分数计算类
 */
class Score
{
    protected $_middleNum = 0;
    protected $_seniorNum = 0;
    protected $units = [
        'ms'  => '分.秒',
        'meter'  => '米',
        'cmeter'  => '厘米',
        'sec' => '秒',
        'an' => '个',
    ];
    public function __construct()
    {
        $this->_middleNum = Env::get('misc.middle_limit_num');
        $this->_seniorNum = Env::get('misc.senior_limit_num');
    }
    // scores 体育项记录
    // areaId 区域id
    // scoreType 考试类别
    // sex 性别
    public function calScore($scores, $areaId, $scoreType, $sex) 
    {
        $res = [
            'msg'  => '',
            'res'  => [
                'score_total' => 0,
                'level_code' => '',
                'level_name' => '',
            ],
        ];
        // 查找区域中中考和高考的评级设置以及中考的固定分
        $areaModel = AreaModel::where('id', $areaId)->where('status', 'normal')->find();
        // 判断有无区域的数据
        if (empty($areaModel)) {
            $res['msg'] = '无对应区域的设置';
            return $res;
        }
        $fixedScore = $areaModel->middle_fixed_score;
        $middleLevelSetting = $areaModel->level_str;
        $seniorLevelSetting = $areaModel->high_level_str;
        // 取出对应考试的所有体育项
        $scoreRows = ScoreModel::where('sport_area_id', $areaId)
                      ->where('status', 'normal')
                      ->where('sex', $sex)
                      ->where('score_type', $scoreType)
                      ->select();
        $scoreItems = [];
        foreach ($scoreRows as $sr) {
            $scoreItems[] = [
                'score_id' => $sr->id,
                'sport_type_id'  => $sr->sport_type_id,
                'unit' => $sr->unit,
                'show_unit' => empty($sr->show_unit) ? $this->units[$sr->unit] : $sr->show_unit,
                'score_setting' => $sr->score_setting,
            ];
        }
        // 调用对应中考还是高考函数
        $calRes = [];
        if ($scoreType == 'middle') {
            if (empty($middleLevelSetting)) {
                $res['msg'] = '未设置评级区间';
                return $res;
            }
            $calRes = $this->calMiddleScore($scores, $areaModel->middle_fixed_score, $scoreItems, $middleLevelSetting, 0, $areaModel->middle_percent);
        } else {
            if (empty($seniorLevelSetting)) {
                $res['msg'] = '未设置评级区间';
                return $res;
            }
            $calRes = $this->calSeniorScore($scores, $scoreItems, $seniorLevelSetting);
        }
        if ($calRes === false) {
            /*
            if ($scoreType =='middle')  {
                $res['msg'] = '需要满足4个项目类别且只能有4个计分体育项';
            } else {
                $res['msg'] = '不满足计分项';
            }
            */
            $res['msg'] = '请至少输入一项成绩';
        } else {
            $res['res'] = $calRes;
            $res['score_items'] = $scoreItems;
        }

        return $res;
    }
    
    /*
     * (四项总分合计/4)*0.45+15（固定,每个省会有不同）= 总得分
        评级
        60-54 优秀  A 
        53.9-45 良好  B
        44.9-30 合格  C
        29.9-0 不合格 D
     */
    public function calMiddleScore($scores, $fixedScore, $scoreItems, $levelSetting, $totalPoint = 0, $percent = 0)
    {
        $res = [
            'score_total' => 0,
            'level_code' => '',
            'level_name' => '未满4项,暂无评级',
        ];
        if (empty($totalPoint)) {
            // 将scores中score为0的排除 
            $scoresTmp = [];
            foreach ($scores as &$s) {
                if (!empty($s['score'])) {
                    $scoresTmp[] = $s; 
                }
            }
            $scores = $scoresTmp;
            // 将各体育项按类别分组
            $scoreTypes = [];
            foreach ($scoreItems as $si) {
                if (!isset($scoreTypes[$si['sport_type_id']])) {
                    $scoreTypes[$si['sport_type_id']] = [];
                }
                $scoreTypes[$si['sport_type_id']][] = $si['score_id'];
            }
            // 判断输入的体育项是否满足已有项目类别中各有一项
            $newScores = [];
            $newScoreTypes = [];
            foreach ($scores as $s) {
                foreach ($scoreTypes as $sportTypeId => $st) {
                    if (in_array($s['sport_score_id'], $st)) {
                        // 判断是否有同类的
                        if (!isset($newScoreTypes[$sportTypeId])) {
                            $newScoreTypes[$sportTypeId] = 1;
                        } else {
                            $newScoreTypes[$sportTypeId]++;
                        }
                    }
                }
                $newScores[$s['sport_score_id']] = $s;
            }
            // 类别数统计
            $findNum = count($newScoreTypes);
            // 体育项统计 
            $sportsNum = array_sum($newScoreTypes);
            if ($sportsNum == 0) {
                return false;
            }
            /*
            // 开关控制是否要4个体育项
            if ($this->_middleNum > 0 && $findNum != $this->_middleNum) {
                return false;
            }
            if ($this->_middleNum > 0 && $sportsNum != $this->_middleNum) {
                return false;
            }
             */
            // 依次对各体育项单独计分
            foreach ($scoreItems as $si) {
                if (isset($newScores[$si['score_id']])) {
                    $scoreInfo = $newScores[$si['score_id']];
                    // 转换成绩
                    $scoreRes = $this->convertScore($scoreInfo['score'], $si['unit'], $si['show_unit']);
                    $scoreInfo['score'] = $scoreRes[0];
                    $scoreInfo['point'] = $this->calPoint($si['score_setting'], $scoreInfo['score']);
                    $newScores[$si['score_id']] = $scoreInfo;
                }    
            }
            // 公式计算总得分
            $totalPoint = 0;
            foreach($newScores as $s) {
                $totalPoint += $s['point'];
            }
            $calTotalPoint = round((($totalPoint / 4) * $percent) / 100, 2) + $fixedScore;
            $res['score_total'] = number_format($calTotalPoint,2,'.','');
            // 总得分在区设置的评价范围内查找评级
            // 如果满足4项目给出评级
            if ($findNum == $this->_middleNum && $sportsNum == $this->_middleNum) {
                $levelRes = $this->calLevel($calTotalPoint, $levelSetting);
                $res['level_code'] = $levelRes['level_code'];
                $res['level_name'] = $levelRes['level_name'];
            }
        } else {
            // 批量
            $calTotalPoint = round((($totalPoint / 4) * $percent) / 100, 2) + $fixedScore;
            $res['score_total'] = number_format($calTotalPoint,2,'.','');
            $levelRes = $this->calLevel($calTotalPoint, $levelSetting);
            $res['level_code'] = $levelRes['level_code'];
            $res['level_name'] = $levelRes['level_name'];
        }

        return $res;
    }
    
    /*
     *  高考总评分 高考4项是固定
        四项总分合计就是总得分
        评级
        100-95 A+(顶级学府)
        90-94 A（重点本科）
        85-89 A-（一本）
        73-84 B (普通本科)
        0-72 C
     */
    public function calSeniorScore($scores, $scoreItems, $levelSetting, $totalPoint = 0)
    {
        $res = [
            'score_total' => 0,
            'level_code' => '',
            'level_name' => '未满4项,暂无评级',
        ];
        if (empty($totalPoint) && !empty($scoreItems)) {
            $scoreItemsId = [];
            foreach ($scoreItems as $si) {
                $scoreItemsId[] = $si['score_id'];
            }
            $newScores = [];
            $newScoresId = [];
            // 是否在计分的体育项内
            foreach ($scores as $s) {
                if (in_array($s['sport_score_id'], $scoreItemsId) && !in_array($s['sport_score_id'], $newScoresId)) {
                    $newScores[$s['sport_score_id']] = $s;
                    $newScoresId[] = $s['sport_score_id'];
                }
            }
            $findNum = count($newScoresId);
            if ($findNum == 0) {
                return false;
            }
            // 开关控制是否要4个体育项
            /*
            if ($this->_seniorNum > 0 && $findNum != $this->_seniorNum) {
                return false;
            }
             */
            // 依次对各体育项单独计分
            foreach ($scoreItems as $si) {
                if (isset($newScores[$si['score_id']])) {
                    $scoreInfo = $newScores[$si['score_id']];
                    // 转换成绩
                    $scoreRes = $this->convertScore($scoreInfo['score'], $si['unit'], $si['show_unit']);
                    $scoreInfo['score'] = $scoreRes[0];
                    $scoreInfo['point'] = $this->calPoint($si['score_setting'], $scoreInfo['score']);
                    $newScores[$si['score_id']] = $scoreInfo;
                }    
            }
            // 公式计算总得分
            $totalPoint = 0;
            foreach($newScores as $s) {
                $totalPoint += $s['point'];
            }
            $res['score_total'] = sprintf("%.2f", $totalPoint);
            // 总得分在区设置的评价范围内查找评级
            if ($findNum == $this->_seniorNum) {
                $levelRes = $this->calLevel($totalPoint, $levelSetting);
                $res['level_code'] = $levelRes['level_code'];
                $res['level_name'] = $levelRes['level_name'];
            }
        } else {
            $res['score_total'] = sprintf("%.2f", $totalPoint);
            $levelRes = $this->calLevel($totalPoint, $levelSetting);
            $res['level_code'] = $levelRes['level_code'];
            $res['level_name'] = $levelRes['level_name'];
        }


        return $res;
    }
    
    // 自动反馈
    public function createAutoFeedback($scoreUserId, $scoreItems = null)
    {
        $content = "";
        // $scoreItems = ScoreUserDetailModel::where('sport_user_id', $scoreUserId)->with('feedbacks')->select();
        if ($scoreUserId) {
            $scoreItems = ScoreUserDetailModel::where('sport_user_id', $scoreUserId)->select();
        }
        foreach ($scoreItems as $si) {
            $point = $si['point'];
            $feedback = FeedbackModel::where('sport_score_id', $si['sport_score_id'])
              ->where('min_score', '<=', $point)
              ->where('max_score', '>=', $point)
              ->find();
            if (!empty($feedback)) {
                $content .= $feedback->feedback . "<br />";
            }
        }
        $teacherId = 0;
        $teacher = Db::name('sport_teacher')
              ->field('id')
              ->orderRaw('rand()')
              ->find();
        if ($teacher) {
            $teacherId = $teacher['id'];
        }

        return [$content, $teacherId];
    
    }

    // scoreSetting 得分设置
    // score 成绩 由外围保证传入的正确
    // unit 单位
    public function calPoint($scoreSetting, $score)
    {
        $point = '0';
        $scoreSetting = json_decode($scoreSetting, true);
        if (!empty($scoreSetting)) {
            foreach ($scoreSetting as $ss) {
                if (empty($ss['min']) || empty($ss['score'])) {
                    continue;
                }
                // 判断如果输入的最大值和最小值有2个点时, 用字符串比较的方式
                $dotMinTotal = mb_substr_count($ss['min'], ".");
                $dotMaxTotal = mb_substr_count($ss['max'], ".");
                if ($dotMaxTotal == 2 && $dotMaxTotal == 2) {
                    if ($score == $ss['min'] && $score == $ss['max']) {
                        $point = $ss['score'];
                        break;
                    }
                } else {
                    $min = $ss['min'] * 1;
                    if (empty($ss['max'])) {
                        $ss['max'] = 10000;
                    }
                    $max = $ss['max'] * 1;
                    if ($score >= $min && $score <= $max ) {
                        $point = $ss['score'];
                        break;
                    }
                }
            }
        }

        return $point; 
    }
    
    // 根据输入的以及展示的单位进行转换
    // 返回用于计算和用于保存后显示用的
    public function convertScore($score, $unit, $showUnit)
    {
        $res = [$score, $score];
        // 如果末尾传递的0，去掉末尾的点后返回
        $tmpScore = trim($score, ".");
        if ($tmpScore !== $score) {
            $res = [$tmpScore, $tmpScore];
            return $res;
        }
        /*
        // 如果单位中没有点,直接返回 
        if (str_replace(".", $showUnit) == $showUnit) {
            return $res;
        }
         */
        if (empty($showUnit)) {
            $showUnit = $unit;
        }
        // 输入成绩的有点的总数
        $dotScoreTotal = mb_substr_count($score, ".");
        // 显示单位有点的总数
        $dotUnitTotal = mb_substr_count($showUnit, ".");
        // 如果是分钟单位
        $showScore = $score;
        if ($unit == 'ms') {
            $mins = $score;
            // 传入的成绩有点
            if ($dotScoreTotal > 0) {
                $tmpArr = explode(".", $score);
                $countArr = count($tmpArr);
                // 如果是输入成绩的两个点时，并且显示单位也是有2个点时，直接返回成绩，并与评分标准做纯字符串比较
                if ($dotUnitTotal == 2 && $dotScoreTotal == 2) {
                    $mins = $score;
                } else {
                    // 有毫秒的将毫秒转为秒
                    $tmpMin = $tmpArr[0];
                    $tmpSec = $tmpArr[1];
                    // 如果输入的秒大于60, 输入格式错误
                    if ($tmpSec > 60) {
                        $mins = 0;
                    } else {
                        // 如果等于60的时候，直接增加1分钟, 同时秒单位置为0
                        if ($tmpSec == 60) {
                            $tmpMin += 1;
                            $tmpSec = 0;
                        }
                        if ($countArr > 2) {
                            /*
                            // 这里按码表定义传递的应该是
                            $tmpMillSec = intval($tmpArr[2]) * 10 / 1000;
                            // TODO 大于1000时
                            // 转为分钟后与分相加
                            $tmpSec = ($tmpSec + $tmpMillSec) / 100;
                            */
                        }
                        // 如果没有小数
                        if (mb_substr_count($tmpSec, ".") == 0) {
                            $mins = vsprintf("%s.%s", [$tmpMin, $tmpSec]);
                        }  else {
                            $mins = $tmpMin + $tmpSec;
                        }
                    }
                }
            }
            // 根据显示的点数和成绩已有的点数 将成绩转换成显示的点数 
            if ($dotUnitTotal == 2) {
                if ($dotScoreTotal == 0) {
                    $showScore = $score . ".00.00";
                } else if ($dotScoreTotal == 1) {
                    $showScore = $score . ".00";
                }
            }
            $score = $mins;
            $res = [$score, $showScore];
        }
        // 如果是秒单位
        if ($unit == 'sec') {
            $secs = $score;
            // 如果传入的有点
            if ($dotScoreTotal > 0) {
                $tmpArr = explode(".", $score);
                $countArr = count($tmpArr);
                // 只一个点情况
                if ($dotUnitTotal == 1) {
                    // 有毫秒的将毫秒转为秒
                    $tmpSec = $tmpArr[0];
                    // 这里按码表定义传递的应该是
                    $tmpMill = intval($tmpArr[1]) * 10;
                    // TODO 大于1000时
                    $secs = vsprintf("%s.%s", [$tmpSec, $tmpMill / 1000]);
                } else if ($dotUnitTotal == 2){
                    // 有毫秒的将毫秒转为秒
                    $tmpMin = $tmpArr[0];
                    $tmpSec = $tmpArr[1];
                    // 如果输入的秒大于60 输入格式错误
                    if ($tmpSec > 60) {
                        $secs = 0;
                    } else {
                        // 如果等于60的时候，直接增加1分钟, 同时秒单位置为0
                        if ($tmpSec == 60) {
                            $tmpMin += 1;
                            $tmpSec = 0;
                        }
                        if ($countArr > 2) {
                            // 这里按码表定义传递的应该是
                            $tmpMillSec = intval($tmpArr[2]) * 10 / 1000;
                            // TODO 大于1000时
                            $tmpSec = ($tmpSec + $tmpMillSec) / 100;
                        }
                        // 最后转为秒
                        $secs = $tmpMin * 60 + $tmpSec;
                    }
                }
            }
            // 根据显示的点数和成绩已有的点数 将成绩转换成显示的点数 
            if ($dotUnitTotal == 2) {
                if ($dotScoreTotal == 0) {
                    $showScore = $score . ".00.00";
                } else if ($dotScoreTotal == 1) {
                    $showScore = $score . ".00";
                }
            } else if ($dotUnitTotal == 1) {
                if ($dotScoreTotal == 0) {
                    $showScore = $score . ".00";
                }
            }
            $score = $secs;
            $res = [$score, $showScore];
        }
        // 如果是米或厘米单位, 都自动补齐显示的2点
        if ($unit == 'meter' || $unit == 'cmeter') {
            // 没有点,直接补齐2点
            if ($dotScoreTotal == 0) {
                $showScore = $score . ".00";
            }
            $res = [$score, $showScore];
        }
        return $res;
    }
    
    // 计算评级 
    private function calLevel($calTotalPoint, $levelSetting)
    {
        $res = [
            'level_code' => '',
            'level_name' => '',
        ];
        // 总得分在区设置的评价范围内查找评级
        $levelSettingArr = json_decode($levelSetting, true);
        foreach ($levelSettingArr as $ls) {
            $min = $ls['min'] * 1;
            if (empty($ls['max'])) {
                $ls['max'] = 10000;
            }
            $max = $ls['max'] * 1;
            if ($calTotalPoint >= $min && $calTotalPoint <= $max ) {
                $res['level_code'] = $ls['code'];
                $res['level_name'] = $ls['name'];
                break;
            }
        }

        return $res;
    
    }
}

